package hhtc.jsjds.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.time.Instant;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 *
 * @author
 *
 */
@Component
public class JwtUtils implements Serializable {

    @Autowired
    RedisTemplate redisTemplate;


    @Autowired
    JwtAuthenticationConfig config;



    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + config.getExpiration() * 1000);
        return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, config.getSecret()).compact();
    }

    /**
     * 将token存储到redis
     */
    public void setExpire(String key, String val, long time) {
        redisTemplate.opsForValue().set(key, val, time);
    }

    /**
     * 移除
     */
    public void del(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 判断是否有效
     * @param authToken
     * @return
     */
    public Boolean validateToken(String authToken) {
        return true;
//        Object o = redisTemplate.opsForValue().get(authToken);
//        if(null != o){
//            return true;
//        }
//        return false;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(config.getSecret().getBytes()).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }


    /**
     * 私钥加密token
     *
     * @param id 用户id      载荷中的数据
     * @param expireMinutes 过期时间，单位秒 n
     * @return
     * @throws Exception
     */
    public  String generateToken(String id, String username, Collection<? extends GrantedAuthority> authorities, int expireMinutes) throws Exception {
        Instant now = Instant.now();
        String token = Jwts.builder()
                .setSubject(username)
                .claim("authorities", authorities.stream()
                        .map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
                .claim("id",id)
                .setIssuedAt(Date.from(now))
                .setExpiration(Date.from(now.plusSeconds(config.getExpiration())))
                .signWith(SignatureAlgorithm.HS256, config.getSecret().getBytes())
                .compact();
//        redisTemplate.opsForValue().append(token,username);
        return token;
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return !expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 刷新令牌
     *
     * @param token 原令牌
     * @return 新令牌
     */
    public String refreshToken(String token) {
        String refreshedToken;
        try {
            Claims claims = getClaimsFromToken(token);
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    public List<String> getAuthorities(String token ){
        List<String> authorities;
        try {
            Claims claims = getClaimsFromToken(token);
            authorities = claims.get("authorities", List.class);
        } catch (Exception e) {
            authorities = null;
        }
        return  authorities;
    }

}
